/** 
 * 节点资源引用计数管理
 * Author      : donggang
 * Create Time : 2016.11.16
 * 
 * Internal
 * 
 * 设计思路
 * 1、节点或节点组件管理资源，所以节点或节点组件释放时，资源也需要释放
 * 2、每个节点释放时删除当前节点上用过的资源，通过引用计数来管理公用资源是否需要
 * 3、永久缓存的资源只需要设计一个不释放的节点就成为永久缓存资源了
 */
module.exports = cc.Class({
    ctor : function () {
        this._resources = {};           // 游戏资源文件
    },

    /** 
     * 加载单个资源
     * @param url(string)                   资源地址
     * @param type(cc.Asset)                资源类型
     * @param completeCallback(function)    加载完成回调
     * @param resources(Array)              自定对象资源数据（为空时为全局共享资源数据）
     */
    loadRes : function(url, type, completeCallback, resources) {
        cc.loader.loadRes(url, type, function(error, asset){
            if(error){
                cc.error(error.message || error);
            }
            else {
                this._analysis(asset, resources);
                
                if (completeCallback) completeCallback(asset);
            }
        }.bind(this));
    },

    
    /**
     * 加载一个文件夹多个资源
     * @param url(string)                   资源文件夹地址
     * @param type(cc.Asset)                资源类型
     * @param completeCallback(function)    加载完成回调
     * @param resources(Array)              自定对象资源数据（为空时为全局共享资源数据）
     */
    loadResAll : function(url, type, completeCallback, resources) {
        cc.loader.loadResAll(url, type, function(error, items){
            if(error){
                cc.error(error.message || error);
            }
            else {
                for (var i = 0; i < items.length; i++){
                    this._analysis(items[i], resources);
                }
                if (completeCallback) completeCallback(items);
            } 
        }.bind(this));
    },

     /**
     * 加载多个资源
     * @param urls(Array)                   资源地址数组
     * @param completeCallback(function)    加载完成回调
     * @param isShare(boolen)               是否设置为全局共享资源
     * @param resources(Array)              自定对象资源数据（为空时为全局共享资源数据）
     */
    loadResByUrls : function(urls, completeCallback, resources) {
        cc.loader.loadResByUrls(urls, function(items){
            for (var i = 0; i < items.length; i++){
                this._analysis(items[i], resources);
            }
            if (completeCallback) completeCallback(items);
        }.bind(this));
    },

    /** 释放节点对象、节点组件的资源内存 */
    destroy : function(resources){
        for (var url in resources){
            this._release(url);
        }
    },

    /** 
     * 资源在引用计数前分析资源类型
     * @param asset(cc.Asset)   资源实体对象
     */
    _analysis : function(asset, resources){
        if (asset instanceof cc.Prefab){                                    // 预制
            var depends = {};
            this._parseDepends(asset._uuid, depends);
            for (var url in depends){
                this._retain(url, asset, resources);
            }

            this._retain(asset._uuid, asset, resources);
        }
        else if (asset._uuid){                                              // 引擎配置文本数据
            this._retain(asset._uuid, asset, resources);
        }
        else if (asset instanceof cc.Texture2D){                            // 纹理图片
            this._retain(asset.url, asset, resources);
        }
        else {
            this._retain(asset, asset, resources);                          // 其它引擎未知资源
        }
    },

    /** 
     * 节点资源引用增加
     * @param url(string)               资源地址
     * @param asset(cc.Asset)           资源实体对象
     * @param resources(Object)         当前节点资源集合
     */
    _retain : function(url, asset, resources){
        // 同一节点资源加载同一个资源只做一次引用计数
        if (resources[url] == null){
            // 当前对象的引用数据
            resources[url] = asset;

            // 所有资源引用数据
            if (this._resources[url] == null){
                var data       = {};
                data.asset     = asset;
                data.reference = 1;
                this._resources[url] = data;

                // cc.log("【资源】引用{1},次{0}".format(url, data.reference));
            }
            else {
                this._resources[url].reference++;
                // cc.log("【资源】引用{1},次{0}".format(url, this._resources[url].reference));
            }
        }
    },
    
    /** 
     * 节点资源引用释放
     * @param url(string)               资源地址
     * @param asset(cc.Asset)           资源实体对象
     */
    _release : function(url){
        if (this._resources[url]) {
            var data = this._resources[url];
            data.reference--;

            if (data.reference <= 0){
                // 引擎 1.2 版释放资源有BUG，第二次释放同名资源时释放不了
                // if (data.asset instanceof cc.Texture2D){
                //     cc.textureCache.removeTextureForKey(url);
                // }
                // else {
                //     cc.loader.release(url);
                // }

                // 引擎 1.3 版释放资源代码
                cc.loader.release(url);
                delete this._resources[url];
                // cc.log("【资源】当前引用计数为{0}, 引用计数为{1}".format(data.reference, url));
            }
            else {
                // cc.log("【资源】当前引用计数为{0}, 引用计数为{1}".format(data.reference, url));
            }
        }
    },

    /** 解析预制包含的静态资源文件集合 */
    _parseDepends : function(key, parsed) {
        var item = cc.loader.getItem(key);
        if (item) {
            var depends = item.dependKeys;
            if (depends) {
                for (var i = 0; i < depends.length; i++) {
                    var depend = depends[i];
                    if (!parsed[depend]) {
                        parsed[depend] = true;
                        this._parseDepends(depend, parsed);
                    }
                }
            }
        }
    }
});